package io.itit.ecp.admin.provide.action.controller;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import icu.mhb.mybatisplus.plugln.core.JoinLambdaWrapper;
import icu.mhb.mybatisplus.plugln.extend.Joins;
import io.itit.ecp.admin.common.enums.IActivityCarbonEmissionDataTypeEnums;
import io.itit.ecp.admin.common.enums.IActivityCarbonEmissionTypeEnums;
import io.itit.ecp.admin.provide.action.req.CarbonEmissionDataQueryReq;
import io.itit.ecp.admin.provide.action.req.DataFillingDetailReq;
import io.itit.ecp.admin.provide.action.resp.*;
import io.itit.ecp.admin.provide.helper.SceneHelper;
import io.itit.ecp.admin.server.entity.*;
import io.itit.ecp.admin.server.service.*;
import io.itit.grass.common.provide.action.resp.ResultResp;
import io.itit.grass.common.provide.action.validation.IExport;
import io.itit.grass.common.provide.action.validation.IQueryPage;
import io.itit.grass.estelle.starter.core.ExcelXSSFUtil;
import io.itit.grass.estelle.starter.util.JsonUtil;
import io.itit.grass.web.wall.utils.PageUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

@Api(tags = "碳排数据-活动")
@RestController
@RequestMapping("/ecp-admin/carbonEmissionDataActivity")
public class CarbonEmissionDataActivityController {

    @Resource
    private IActivityCarbonEmissionService activityCarbonEmissionService;

    @Resource
    private IActivityCarbonEmissionDataService activityCarbonEmissionDataService;

    @Resource
    private IDataFillingDetailService dataFillingDetailService;

    @Resource
    private IDataFillingService dataFillingService;

    @Resource
    private IMeterDeviceService meterDeviceService;

    @Resource
    private IEnergyConsumptionTypeService energyConsumptionTypeService;

    @Resource
    private IEnergyDataService energyDataService;

    @Resource
    private ICarbonSinkService carbonSinkService;

    @Resource
    private ICarbonSinkDataService carbonSinkDataService;


    @ApiOperation("分页查询")
    @PostMapping("queryPage")
    @RequiresPermissions("ecp-admin:carbonEmissionData:search")
    public ResultResp<IPage<CarbonEmissionDataActivityResp>> queryPage(@RequestBody @Validated(IQueryPage.class) CarbonEmissionDataQueryReq req) {
        IPage<CarbonEmissionDataActivityResp> page = buildQW(req).joinPage(PageUtils.getPage(req, CarbonEmissionDataActivityResp.class), CarbonEmissionDataActivityResp.class);
        return ResultResp.success(SceneHelper.i().setSceneInfo(page));
    }

    @ApiOperation("查看详情")
    @GetMapping("detail")
    public ResultResp<CarbonEmissionDataActivityDetailResp> detail(@RequestParam String id,
                                                                   @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime solidifyDate) {
        ActivityCarbonEmissionEntity carbonEmissionEntity = activityCarbonEmissionService.getById(id);
        if (Objects.isNull(carbonEmissionEntity)) {
            return ResultResp.fail("查询数据不存在");
        }
        List<ActivityCarbonEmissionDataEntity> planDataEntities = activityCarbonEmissionDataService.selectByActivityCarbonId(id);
        List<DataFillingEntity> dataEntities = dataFillingService.selectByActivityCarbonId(id);
        Set<String> dataFillingIds = dataEntities.stream().map(DataFillingEntity::getId).collect(Collectors.toSet());
        List<DataFillingDetailEntity> dataFillingDataEntities = dataFillingIds.isEmpty() ? Collections.emptyList() :
                dataFillingDetailService.selectByDataFillingIds(dataFillingIds);
        CarbonEmissionDataActivityDetailResp resp = new CarbonEmissionDataActivityDetailResp();
        resp.setId(id);
        resp.setActivityType(carbonEmissionEntity.getActivityType());
        BigDecimal total = BigDecimal.ZERO;
        Set<ActivityCarbonEmissionDataEntity> deviceList = planDataEntities.stream()
                .filter(item -> StringUtils.equals(item.getDataType(), IActivityCarbonEmissionDataTypeEnums.RELATED_MEASURING_EQUIPMENT.getValue())).collect(Collectors.toSet());
        Set<String> deviceIds = deviceList.stream().map(ActivityCarbonEmissionDataEntity::getTargetId).collect(Collectors.toSet());
        List<MeterDeviceEntity> deviceEntities = deviceIds.isEmpty() ? Collections.emptyList() : meterDeviceService.listByIds(deviceIds);

        List<EnergyConsumptionTypeEntity> typeEntities = energyConsumptionTypeService
                .lambdaQuery()
                .eq(EnergyConsumptionTypeEntity::getStatus, "1")
                .orderByAsc(EnergyConsumptionTypeEntity::getCreateTm).list();

        List<EnergyDataEntity> energyDataEntities = deviceIds.isEmpty() ? Collections.emptyList() : energyDataService.list(new LambdaQueryWrapper<EnergyDataEntity>()
                .in(EnergyDataEntity::getMeterDeviceId, deviceIds)
                .ge(EnergyDataEntity::getSolidifyDate, solidifyDate.minusMinutes(15))
                .le(EnergyDataEntity::getSolidifyDate, solidifyDate)
        );

        List<ActivityCarbonEmissionDataResp> list = deviceList.stream().map(item -> {
                    MeterDeviceEntity meterDevice = deviceEntities.stream().filter(device -> device.getId().equals(item.getTargetId())).findFirst().orElse(null);
                    ActivityCarbonEmissionDataResp dataResp = new ActivityCarbonEmissionDataResp()
                            .setSceneProjectId(item.getSceneProjectId())
                            .setSceneEdificeId(item.getSceneEdificeId())
                            .setSceneFloorId(item.getSceneFloorId())
                            .setSceneRoomId(item.getSceneRoomId());
                    if (Objects.nonNull(meterDevice)) {
                        EnergyConsumptionTypeEntity consumptionTypeEntity = typeEntities.stream().filter(typeEntity -> StringUtils.equals(meterDevice.getEnergyTypeId(), typeEntity.getId())).findFirst().orElse(null);
                        dataResp.setDeviceCode(meterDevice.getDeviceCode())
                                .setDeviceName(meterDevice.getDeviceName())
                                .setEnergyTypeId(meterDevice.getEnergyTypeId())
                                .setStaticsType(meterDevice.getStaticsType());
                        if (Objects.nonNull(consumptionTypeEntity)) {
                            dataResp.setEnergyTypeName(consumptionTypeEntity.getEnergyName())
                                    .setEnergyTypeUnit(consumptionTypeEntity.getEnergyUnit());
                        }
                        EnergyDataEntity energyDataEntity = energyDataEntities.stream().filter(energyData -> StringUtils.equals(meterDevice.getId(), energyData.getMeterDeviceId())).findFirst().orElse(null);
                        dataResp.setCarbonEmission(Objects.isNull(energyDataEntity) ? BigDecimal.ZERO : ObjectUtil.defaultIfNull(energyDataEntity.getCarbonEmission(), BigDecimal.ZERO));
                        dataResp.setMeterValue(Objects.isNull(energyDataEntity) ? BigDecimal.ZERO : ObjectUtil.defaultIfNull(energyDataEntity.getMeterValue(), BigDecimal.ZERO));
                    }
                    return dataResp;
                }

        ).collect(Collectors.toList());
        resp.setDeviceList(SceneHelper.i().setSceneInfo(list));
        total = total.add(list.stream().map(ActivityCarbonEmissionDataResp::getCarbonEmission).reduce(BigDecimal.ZERO, BigDecimal::add));
        Set<ActivityCarbonEmissionDataEntity> greenList = planDataEntities.stream()
                .filter(item -> StringUtils.equals(item.getDataType(), IActivityCarbonEmissionDataTypeEnums.RELATED_GREENING.getValue())).collect(Collectors.toSet());
        Set<String> sinkIds = greenList.stream().map(ActivityCarbonEmissionDataEntity::getTargetId).collect(Collectors.toSet());
        List<CarbonSinkEntity> sinkEntities = sinkIds.isEmpty() ? Collections.emptyList() : carbonSinkService.listByIds(sinkIds);
        list = greenList.stream().map(item -> {
                    CarbonSinkEntity sinkEntity = sinkEntities.stream().filter(sinkData -> sinkData.getId().equals(item.getTargetId()))
                            .findFirst().orElse(null);
                    ActivityCarbonEmissionDataResp dataResp = new ActivityCarbonEmissionDataResp()
                            .setSceneProjectId(item.getSceneProjectId())
                            .setSceneEdificeId(item.getSceneEdificeId())
                            .setSceneFloorId(item.getSceneFloorId())
                            .setSceneRoomId(item.getSceneRoomId());
                    if (Objects.nonNull(sinkEntity)) {
                        dataResp.setCarbonSinkId(sinkEntity.getCarbonSinkId());
                        dataResp.setType(sinkEntity.getType());
                        dataResp.setArea(sinkEntity.getArea());
                        dataResp.setCarbonEmission(carbonSinkDataService.lambdaQuery()
                                .eq(CarbonSinkDataEntity::getType,sinkEntity.getId()).list()
                                .stream().map(CarbonSinkDataEntity::getCarbonEmission)
                                .filter(Objects::nonNull)
                                .reduce(BigDecimal.ZERO,BigDecimal::add));
                    }
                    return dataResp;
                }
        ).collect(Collectors.toList());
        resp.setRelatedGreeningList(SceneHelper.i().setSceneInfo(list));
        total = total.add(list.stream().map(ActivityCarbonEmissionDataResp::getCarbonEmission)
                .filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add));
        resp.setDataFillList(SceneHelper.i().setSceneInfo(dataEntities.stream()
                .map(item -> new DataFillingActivityResp()
                        .setSceneProjectId(item.getSceneProjectId())
                        .setSceneEdificeId(item.getSceneEdificeId())
                        .setSceneFloorId(item.getSceneFloorId())
                        .setSceneRoomId(item.getSceneRoomId())
                        .setOutsourcedCarbonSinks(item.getOutsourcedCarbonSinks())
                ).collect(Collectors.toList())));
        total = total.add(resp.getDataFillList().stream().map(DataFillingActivityResp::getOutsourcedCarbonSinks)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add));
        resp.setOtherList(SceneHelper.i().setSceneInfo(dataFillingDataEntities.stream()
                .map(item -> {
                    DataFillOtherResp otherResp = new DataFillOtherResp()
                            .setUsageValue(item.getUsageValue())
                            .setCarbonEmission(item.getCarbonEmission())
                            .setEnergyConsumptionTypeId(item.getEnergyConsumptionTypeId());
                    otherResp.setId(item.getId());
                    DataFillingEntity fillingEntity = dataEntities.stream().filter(dataFilling -> StringUtils.equals(dataFilling.getId(), item.getDataFillingId())).findFirst().orElse(null);
                    if (Objects.nonNull(fillingEntity)) {
                        otherResp.setSceneProjectId(fillingEntity.getSceneProjectId())
                                .setSceneEdificeId(fillingEntity.getSceneEdificeId())
                                .setSceneFloorId(fillingEntity.getSceneFloorId())
                                .setSceneRoomId(fillingEntity.getSceneRoomId());
                    }
                    EnergyConsumptionTypeEntity consumptionTypeEntity = typeEntities.stream()
                            .filter(typeEntity -> StringUtils.equals(item.getEnergyConsumptionTypeId(), typeEntity.getId())).findFirst().orElse(null);
                    if (Objects.nonNull(consumptionTypeEntity)) {
                        otherResp.setEnergyTypeName(consumptionTypeEntity.getEnergyName())
                                .setEnergyTypeUnit(consumptionTypeEntity.getEnergyUnit());
                    }
                    return otherResp;
                }).collect(Collectors.toList())));
        total = total.add(resp.getOtherList().stream().map(DataFillOtherResp::getCarbonEmission).reduce(BigDecimal.ZERO, BigDecimal::add));
        if (StringUtils.equals(carbonEmissionEntity.getActivityType(), IActivityCarbonEmissionTypeEnums.CARBON_OFFSETTING.getValue())) {
            total = total.negate();
        }
        resp.setTotal(total);
        return ResultResp.success(resp);
    }


    @Transactional
    @PostMapping("batchUpdate")
    public ResultResp<Boolean> batchUpdate(@RequestBody List<DataFillingDetailReq> detailReqs) {
        for (DataFillingDetailReq item : detailReqs) {
            dataFillingDetailService.update(new LambdaUpdateWrapper<DataFillingDetailEntity>()
                    .set(DataFillingDetailEntity::getUsageValue, item.getUsageValue())
                    .eq(DataFillingDetailEntity::getId, item.getId())
            );
        }
        return ResultResp.success(Boolean.TRUE);
    }

    private static JoinLambdaWrapper<EnergyDataEntity> buildQW(CarbonEmissionDataQueryReq req) {
        return Joins.of(EnergyDataEntity.class)
                .selectAll()
                .leftJoin(SceneProjectEntity.class, SceneProjectEntity::getId, EnergyDataEntity::getSceneProjectId)
                .selectAs(SceneProjectEntity::getProjectName, CarbonEmissionDataActivityResp::getProjectName)
                .end()
                .innerJoin(ActivityCarbonEmissionDataEntity.class, ActivityCarbonEmissionDataEntity::getTargetId,
                        EnergyDataEntity::getMeterDeviceId)
                .selectAs(ActivityCarbonEmissionDataEntity::getActivityCarbonId, CarbonEmissionDataActivityResp::getActivityCarbonEmissionId)
                .end()
                .innerJoin(ActivityCarbonEmissionEntity.class, ActivityCarbonEmissionEntity::getId, ActivityCarbonEmissionDataEntity::getActivityCarbonId)
                .selectAs(ActivityCarbonEmissionEntity::getActivityName, CarbonEmissionDataActivityResp::getActivityCarbonEmissionName)
                .end()
                .eq(StringUtils.isNotEmpty(req.getSceneProjectId()), EnergyDataEntity::getSceneProjectId, req.getSceneProjectId())
                .eq(StringUtils.isNotEmpty(req.getSceneEdificeId()), EnergyDataEntity::getSceneEdificeId, req.getSceneEdificeId())
                .eq(StringUtils.isNotEmpty(req.getSceneFloorId()), EnergyDataEntity::getSceneFloorId, req.getSceneFloorId())
                .eq(StringUtils.isNotEmpty(req.getSceneRoomId()), EnergyDataEntity::getSceneRoomId, req.getSceneRoomId())
                .ge(req.getBeginTime() != null, EnergyDataEntity::getSolidifyDate, req.getBeginTime())
                .le(req.getEndTime() != null, EnergyDataEntity::getSolidifyDate, req.getEndTime())
                .last(StringUtils.isNotEmpty(req.getSortColumn()), PageUtils.lastOrderBy(req))
                .orderByDesc(StringUtils.isEmpty(req.getSortColumn()), EnergyDataEntity::getSolidifyDate);
    }

    @ApiOperation("导出")
    @PostMapping("export")
    public void export(@RequestBody @Validated(IExport.class) CarbonEmissionDataQueryReq req, HttpServletResponse response) {
        List<CarbonEmissionDataActivityResp> list = SceneHelper.i().setSceneInfo(buildQW(req).joinList(CarbonEmissionDataActivityResp.class));
        ExcelXSSFUtil.export("CarbonEmissionDataActivityExport", JsonUtil.toJson(list), response);
    }

}
